package Compile.YUYI;

import Compile.CIFA.TokenType;
import Compile.YUFA.TreeNode;
import Execute.MiniSQL.TableManager;
import Execute.MiniSQL.module.Insert;

import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;

public class yuyi {
    // 语法树根节点
    private TreeNode root;
    private TreeNode curNode;
    // 语义分析处理结果
    private String content;
    private boolean flag = false;

    public yuyi(TreeNode root) {
        this.root = root;
        content = "";
        SEMANTIC();
    }

    public String getContent() {
        return this.content;
    }

    public boolean getFlag() {
        return this.flag;
    }

    private void ERROR(String s) {
        content = "语义分析未通过: " + s;
        flag = true;
    }

    /*
    * 语义分析主方法
    * */
    private void SEMANTIC() {
        // root: E -> [S, S, ....]
        for (TreeNode S : root.getChildren()) {
            curNode = S.getChildren().get(0);
            switch (curNode.getValue()) {
                case "quit", "help", "show" -> content = "语义分析通过.";
                case "create" -> {
                    analyzeCreate(S);
                    if (flag) return;
                    content = "语义分析通过：create";
                }
                case "insert" -> {
                    analyzeInsert(S);
                    if (flag) return;
                    content = "语义分析通过：insert";
                }
                case "update" -> {
                    analyzeUpdate(S);
                    if (flag) return;
                    content = "语义分析通过：update";
                }
                case "delete" -> {
                    analyzeDelete(S);
                    if (flag) return;
                    content = "语义分析通过：delete";
                }
                case "select" -> {
                    analyzeSelect(S);
                    if (flag) return;
                    content = "语义分析通过：select";
                }
                case "drop" -> {
                    analyzeDrop(S);
                    if (flag) return;
                    content = "语义分析通过：drop";
                }
                case "desc" -> {
                    analyzeDesc(S);
                    if (flag) return;
                    content = "语义分析通过：desc";
                }
                default -> ERROR("unexpected starting point of syntaxTree!");
            }
        }
    }

    /*
    * 分析create语句
    * 表名是否存在
    * 列名是否重复
    * */
    private void analyzeCreate(TreeNode S) {
        if (!checkTreeLen(S, 3)) {
            ERROR("语法树长度错误");
            return;
        }
        String tableName = S.getChildren().get(1).getValue();
        if (checkTableName(tableName)) {
            ERROR("tablename: \"" + tableName + "\" already exist!");
        }
        if (flag) return;
        // 列名是否重复
        List<TreeNode> colList = S.getChildren().get(2).getChildren();
        for (int i = 0; i < colList.size(); i++) {
            for (int j = i+1; j < colList.size(); j++) {
                if (colList.get(i).getValue().equalsIgnoreCase(colList.get(j).getValue())) {
                    ERROR("列名称重复！");
                    return;
                }
            }
        }
    }

    /*
    * 分析insert语句
    * 1、插入表是否存在
    * 2、插入值数量是否匹配
    * 2、插入数据类型是否匹配
    * */
    private void analyzeInsert(TreeNode S) {
        if (!checkTreeLen(S, 3)) {
            ERROR("语法树长度错误");
            return;
        }
        String tableName = S.getChildren().get(1).getValue();
        // 表是否存在
        if (checkTableName(tableName)) {
            List<TreeNode> valueList = S.getChildren().get(2).getChildren();
            // 获取表结构
            String v = TableManager.preDesc(tableName);     // v = "Student id int Height decimal name varchar(20)"
            String[] t = v.split(" ");
            // 插入值数量
            if (valueList.size() != (t.length-1) / 2) {
                ERROR("您想插入的数据值数量与表结构不匹配。应为 " + (t.length-1)/2 + " 个值。而您提供了 " + valueList.size() + " 个。");
                return;
            }
            // 插入类型
            int j = 0;
            for (int i = 2; i < t.length; i += 2) {
                j = i / 2 - 1;      // 对应i值在valuelist中的下标
                switch (t[i].toLowerCase()) {
                    case "int":
                        try {
                            int m = Integer.parseInt(valueList.get(j).getValue());
                        } catch (Exception e) {
                            ERROR("您提供的第" + (j+1) + "个值不能转换为整数");
                            return;
                        }
                        break;
                    case "decimal":
                        try {
                            double d = Double.parseDouble(valueList.get(j).getValue());
                        } catch (Exception e) {
                            ERROR("您提供的第" + (j+1) + "个值不能转换为小数");
                            return;
                        }
                        break;
                    default:
                        try {
                            String s = valueList.get(j).getValue();
                            if (s.startsWith("'") && s.endsWith("'")) {
                                int LEN = TableManager.getVarchar(t[i]);
                                int len = s.length() - 2;       // 减去两个引号算出字符长度
                                if(len > LEN) {
                                    ERROR("您提供的第" + (j+1) + "个字符串长度已达 " + len + " 超过该数据表此列的可接收最大长度 " + LEN);
                                    return;
                                }
                            } else {
                                ERROR("您提供的第" + (j+1) + "个字符串格式不对，应该用英文单引号包裹");
                                return;
                            }
                        } catch (Exception e) {
                            ERROR("您提供的第" + (j+1) + "个值不能转换为字符串格式");
                            return;
                        }
                        break;
                }
            }
        } else ERROR(tableName + " 表不存在！");
    }

    /*
    * 分析Update语句
    * 1、检查表是否存在
    * 2、修改列是否存在
    * 3、修改列类型是否正确
    * 4、条件列是否存在
    * 5、条件列类型是否正确
    * */
    private void analyzeUpdate(TreeNode S) {
        if (!checkTreeLen(S, 3, 4)) {
            ERROR("语法树长度错误");
            return;
        }
        String tableName = S.getChildren().get(1).getValue();
        if (checkTableName(tableName)) {
            UpdateCol(S, tableName);
            if (flag) return;
            if (S.getChildren().size() == 4) {
                WhereClause(S, tableName, 3);
            }
        } else ERROR(tableName + " 表不存在！");
    }

    /*
    * 1、修改列是否存在
    * 2、修改列类型是否正确
    * */
    private void UpdateCol(TreeNode S, String tableName) {
        List<TreeNode> update = S.getChildren().get(2).getChildren();
        checkCol(update, tableName, 1);
    }

    /*
    * 4、条件列是否存在
    * 5、条件列类型是否正确
    * */
    private void WhereClause(TreeNode S, String tableName, int wherePos) {
        List<TreeNode> where = S.getChildren().get(wherePos).getChildren();
        checkCol(where, tableName, 2);
    }

    /*
    * 列是否存在
    * */
    private void checkColExist(TreeNode S, String tableName, int pos) {
        List<TreeNode> treeNodeList = S.getChildren().get(pos).getChildren();
        // 获取表结构
        String v = TableManager.preDesc(tableName);     // v = "Student id int Height decimal name varchar(20)"
        String[] t = v.split(" ");
        // 列是否存在
        for (int i = 0; i < treeNodeList.size(); i++) {
            if (!v.toLowerCase().contains(treeNodeList.get(i).getValue().toLowerCase())) {
                ERROR("列 " + treeNodeList.get(i).getValue() + " 不存在");
            }
        }
    }

    /*
    * 列是否存在
    * 列类型是否正确
    * */
    private void checkCol(List<TreeNode> treeNodeList, String tableName, int type) {
        // 获取表结构
        String v = TableManager.preDesc(tableName);     // v = "Student id int Height decimal name varchar(20)"
        String[] t = v.split(" ");
        // 列是否存在
        for (int i = 0; i < treeNodeList.size();) {
            if (!v.toLowerCase().contains(treeNodeList.get(i).getValue().toLowerCase())) {
                ERROR("列 " + treeNodeList.get(i).getValue() + " 不存在");
            }
            if (type == 1) i+=2;
            else i+=3;
        }
        // 列类型是否正确
        for (int i = 0; i < treeNodeList.size();) {
            int num,k;
            if (type == 1) {
                num = i / 2 + 1;
                k = i + 1;
            }
            else {
                num = i / 3 + 1;
                k = i + 2;
            }
            for (int j = 1; j < t.length; j += 2) {
                if (treeNodeList.get(i).getValue().equalsIgnoreCase(t[j])) {
                    switch (t[j+1].toLowerCase()) {
                        case "int":
                            try {
                                int m = Integer.parseInt(treeNodeList.get(k).getValue());
                            } catch (Exception e) {
                                ERROR("您提供的第" + num + "个值不能转换为整数");
                                return;
                            }
                            break;
                        case "decimal":
                            try {
                                double d = Double.parseDouble(treeNodeList.get(k).getValue());
                            } catch (Exception e) {
                                ERROR("您提供的第" + num + "个值不能转换为小数");
                                return;
                            }
                            break;
                        default:
                            try {
                                String s = treeNodeList.get(k).getValue();
                                if (s.startsWith("'") && s.endsWith("'")) {
                                    int LEN = TableManager.getVarchar(t[j+1]);
                                    int len = s.length() - 2;       // 减去两个引号算出字符长度
                                    if(len > LEN) {
                                        ERROR("您提供的第" + num + "个字符串长度已达 " + len + " 超过该数据表此列的可接收最大长度 " + LEN);
                                        return;
                                    }
                                } else {
                                    ERROR("您提供的第" + num + "个字符串格式不对，应该用英文单引号包裹");
                                    return;
                                }
                            } catch (Exception e) {
                                ERROR("您提供的第" + num + "个值不能转换为字符串格式");
                                return;
                            }
                            break;
                    }
                }
            }
            if (type == 1) i+=2;
            else i+=3;
        }
    }

    /*
    * 语法树长度 = 3
    * 表是否存在
    * where条件列是否存在、类型是否匹配
    * */
    private void analyzeDelete(TreeNode S) {
        if (!checkTreeLen(S, 3)) {
            ERROR("语法树长度错误！");
            return;
        }
        String tableName = S.getChildren().get(1).getValue();
        if (checkTableName(tableName)) {
            WhereClause(S, tableName, 2);
        } else ERROR("表 " + tableName + " 不存在!");
    }

    /*
    * 分析Select语句
    * 树长度 = 5
    * 1、selectList 为 * or COL_SELECT_LIST列名存在、类型匹配
    * 2、表是否存在
    * 3、where子句列是否存在，类型是否匹配
    * */
    private static final List<String> operator = Arrays.asList(">", "<", "=", ">=", "<=", "<>");
    private void analyzeSelect(TreeNode S) {
        if (!checkTreeLen(S, 5)) {
            ERROR("语法树长度错误！");
            return;
        }
        String tableName = S.getChildren().get(2).getValue();
        if (checkTableName(tableName)) {
            try {
                String type = S.getChildren().get(1).getChildren().get(0).getValue();
                String type2 = S.getChildren().get(1).getChildren().get(1).getValue();
                if (!type.equalsIgnoreCase("*"))
                    if (operator.contains(type2))
                        WhereClause(S, tableName, 1);
                    else {
                        checkColExist(S, tableName, 1);
                    }
            } catch (Exception ignored) {

            }
            if (flag) return;
            WhereClause(S, tableName, 3);
        } else ERROR(tableName + " 表不存在！");
    }

    /*
    * 分析drop语句
    * 语法树长度
    * 表是否存在
    * */
    private void analyzeDrop(TreeNode S) {
        if (!checkTreeLen(S, 2)) {
            ERROR("语法树长度错误！");
            return;
        }
        String tableName = S.getChildren().get(1).getValue();
        if (!checkTableName(tableName)) ERROR(tableName + " 表不存在！");
    }

    /*
     * 分析desc语句
     * 语法树长度
     * 表是否存在
     * */
    private void analyzeDesc(TreeNode S) {
        if (!checkTreeLen(S, 2)) {
            ERROR("语法树长度错误！");
            return;
        }
        String tableName = S.getChildren().get(1).getValue();
        if (!checkTableName(tableName)) ERROR(tableName + " 表不存在！");
    }


    private boolean checkTreeLen(TreeNode S, int len) {
        return S.getChildren().size() == len;
    }

    private boolean checkTreeLen(TreeNode S, int len, int len2) {
        return S.getChildren().size() == len || S.getChildren().size() == len2;
    }

    private boolean checkTableName(String s) {
        // 表名称是否存在
        return TableManager.repeat(s);
    }
}
